import java.awt.Point;
import java.util.ArrayList;

import me.w70.bot.methods.Calc;
import me.w70.bot.methods.Methods;
import me.w70.bot.scripting.Random;
import me.w70.bot.wrappers.RSInterface;
import me.w70.bot.wrappers.RSInterfaces;
import me.w70.bot.wrappers.RSObject;
import me.w70.bot.wrappers.RSPlayer;
import me.w70.hooks.Tile;


public class Pinball extends Random {
	public enum State {
		LEAVING, TAG
	}

	public static final int IFACE_PINBALL = 263;
	public static final int OBJ_EXIT = 15010;

	private static final int[] OBJ_PILLARS = { 15000, 15002, 15004, 15006,
		15008 };;

		private long lastTag;
		private Tile lastTile;
		private State state;

		@Override
		public boolean canRun() {
			if (isLoggedIn() && getPillar() != null) {
				state = State.TAG;
				lastTag = System.currentTimeMillis();
				lastTile = new Tile(0, 0);
				return true;
			}
			return false;
		}

		// Left-click only - stops right-clicking and canceling.
		@Override
		public boolean atTile(final Tile tile, final String action) {
			try {
				// In case of one-button mode.
				if (atMenu(action)) {
					return true;
				}

				final Point location = Calc.tileToScreen(tile);
				if (location.x == -1 || location.y == -1) {
					return false;
				}
				moveMouse(location, 3, 3);
				if (getMenuItems().get(0).toLowerCase().contains(
						action.toLowerCase())) {
					clickMouse(location, 3, 3, true);
				} else {
					return false;
				}
				wait(random(500, 1000));
				while (true) {
					if (!getMyPlayer().isMoving()) {
						break;
					}
					wait(random(500, 1000));
				}
				return true;
			} catch (final Exception e) {
				return false;
			}
		}

		private Tile calculateExitTile() {
			final RSObject air = findObject(15005);

			if (air == null) {
				return null;
			}

			final int x = air.getLocation().getX();
			final int y = air.getLocation().getY() - 13;
			return new Tile(x, y);
		}

		private Tile[] generatePath(final int destinationX, final int destinationY) {
			int startX = getMyPlayer().getLocation().getX();
			int startY = getMyPlayer().getLocation().getY();
			double dx, dy;

			final ArrayList<Tile> list = new ArrayList<Tile>();

			list.add(new Tile(startX, startY));
			while (Math.hypot(destinationY - startY, destinationX - startX) > 2) {
				dx = destinationX - startX;
				dy = destinationY - startY;

				while (Math.hypot(dx, dy) > 2) {
					dx *= .95;
					dy *= .95;
				}
				startX += (int) dx;
				startY += (int) dy;
				list.add(new Tile(startX, startY));
			}
			list.add(new Tile(destinationX, destinationY));
			return list.toArray(new Tile[list.size()]);
		}

		@Override
		public String getScriptAuthor() {
			return "spooky";
		}

		@Override
		public String getScriptName() {
			return "Pinball";
		}

		private RSObject getPillar() {
			return findObject(16, Pinball.OBJ_PILLARS);
		}

		private int getScore() {
			final RSInterface score = RSInterfaces.getInterface(
					Pinball.IFACE_PINBALL, 1);
			return Integer.parseInt(score.getText().split(" ")[1]);
		}


		@Override
		public int loop() {
			final RSPlayer myPlayer = getMyPlayer();
			if (myPlayer.isMoving() || myPlayer.getAnimation() != -1) {
				lastTag = System.currentTimeMillis();
				wait(500);
				return random(200, 300);
			}

			if (state.equals(State.TAG)) {
				final int score = getScore();

				if (score >= 10) {
					state = State.LEAVING;
					return 0;
				}

				final RSObject pillar = getPillar();
				final Tile tile = pillar.getLocation();

				if (tile.equals(lastTile)
						&& System.currentTimeMillis() - lastTag < 2000) {
					return random(300, 400);
				}

				if (recheckPillar(tile)) {
					if (atObject(pillar, "Tag")) {
						lastTile = tile;
						lastTag = System.currentTimeMillis();
						return random(500, 600);
					}
				}
			} else if (state.equals(State.LEAVING)) {
				final Tile exit = calculateExitTile();

				if (exit == null) {
					return -1;
				}

				if (distanceTo(exit) > 3) {
					walkGroundPath(generatePath(exit.getX(), exit.getY()));
					return random(500, 600);
				} else {
					atTile(exit, "Exit");
				}
			}
			return 0;
		}

		private boolean recheckPillar(final Tile tile) {
			final RSObject obj = getObjectAt(tile);
			for (final int i : Pinball.OBJ_PILLARS) {
				if (i == obj.getID()) {
					return true;
				}
			}
			return false;
		}

		private boolean walkGroundPath(Tile[] path) {
			path = randomizePath(path, 1, 1);
			final Tile start = getMyPlayer().getLocation();

			if (Methods.distanceBetween(start, path[path.length - 1]) < 2) {
				return true;
			}

			for (int i = path.length - 1; i > 0; i--) {
				if (tileOnScreen(path[i])) {
					atTile(path[i], "Walk");
					return false;
				}
			}

			throw new RuntimeException();
		}

		@Override
		public String getScriptCategory() {
			// TODO Auto-generated method stub
			return "Random";
		}

		@Override
		public String getScriptDescription() {
			// TODO Auto-generated method stub
			return "Solves the Pinball Random";
		}
}